library(tidyverse)
library(dygraphs)
library(DT)
library(fivethirtyeight)
library(gapminder)
library(gt)
library(maps)
library(plotly)
library(threejs)
library(tidyquant)
library(shiny)
library(viridis)10. Reports
David Rossell
Pompeu Fabra University
Load required R packages
R provides useful formats
Dashboard: short website displaying your main figures/tables/data
htmlwidgets: interactive html documents
Shiny apps: applications allowing for more user input
We cover a gentle intro. Further resources
Hadley Wickam’s chapter on Quarto formats
File -> New file -> Quarto document. Select html format
Your document should start like this
The echo: false option disables the printing of code (only output is displayed)
To display individual code chunks use #| echo: true
You can create multiple tabs as follows
Write :::panel-tabset at start and ::: at the end
Start a new tab with ## Title. Then put R code
:::panel-tabset
## Scatterplot
```{r}
ggplot(diamonds, aes(carat, price)) +
geom_point()
```
## Histogram
```{r}
ggplot(diamonds, aes(price)) +
geom_histogram()
```
:::The next slide shows the result
Code chunks have layout attributes to arrange the output
#| layout-nrow: number of rows
#| layout-nrow: number of columns
#| layout: []: rows / columns of different sizes
It may also be useful to define captions
#| fig-cap: text for figure caption
#| cap-location: where to place to caption (top, bottom or margin)
#| fig-subcap: figure sub-captions
2 plots in a single row, with figure captions
```{r}
#| layout-nrow: 1
#| fig-cap:
#| - "Histogram of prices"
#| - "Prices vs carats"
ggplot(diamonds, aes(price)) +
geom_histogram()
ggplot(diamonds, aes(carat, price)) +
geom_point()
```The result is shown in the next slide


Custom layouts: specify for each row the size of each figure
Example: 2 figures in 1st row of size 0.5 each, 1 figure in 2nd row



gt in package gt: styling transformations to display a table (column names, alignment etc.)
datatable in package DT: display data as html. Provides filtering, pagination, search & sorting
An example by Hadley Wickam: file ../examples/diamonds_dashboard.qmd
Some packages to create interactive plots without using any Javascript (if you know Javascript, then more advanced options are possible)
dygraph visualizes times series interactively
1st argument is the data. Either a time series (xts object) or a data.frame
Other arguments to set labels etc.
Example. Female and male deaths from lung cancer
Zoom into x-axis regions. Double-click to zoom out
Plotly dynamically displays info about elements of a gg-plot. See here for a gallery of what’s possible
Example. Scatter-plot for gapminder data
Save the ggplot into an object, then use ggplotly. Argument tooltip: what aesthetics from your ggplot (not from your original dataset) to show
library(plotly)
gm2007= filter(gapminder, year==2007)
p= ggplot(gm2007, aes(gdpPercap, lifeExp, text=country)) +
geom_point(aes(colour=continent, size=pop), alpha = 0.7, show.legend = TRUE) +
scale_size(range = c(2, 12)) +
scale_x_log10() +
labs(x='GDP per capita', y='Life expectancy')
ggplotly(p, tooltip=c("country","gdpPercap","lifeExp"))Alternatively, you can use plot_ly and layout to create plots directly, as illustrated below.
GOOG.Open GOOG.High GOOG.Low GOOG.Close GOOG.Volume GOOG.Adjusted
2020-01-02 67.0775 68.4070 67.0775 68.3685 28132000 68.3685
2020-01-03 67.3930 68.6250 67.2772 68.0330 23728000 68.0330
2020-01-06 67.5000 69.8250 67.5000 69.7105 34646000 69.7105
2020-01-07 69.8970 70.1495 69.5190 69.6670 30054000 69.6670
2020-01-08 69.6040 70.5790 69.5420 70.2160 30560000 70.2160
Compute stock market value relative to start date
fig= plot_ly(stock, type = 'scatter', mode = 'lines') |>
add_trace(x = ~date, y = ~GOOG, name = 'GOOG') |>
layout(showlegend = F)
options(warn = -1)
layout(fig, xaxis = list(zerolinecolor = '#ffff', zerolinewidth = 2, gridcolor = 'ffff'), yaxis = list(zerolinecolor = '#ffff', zerolinewidth = 2, gridcolor = 'ffff'), plot_bgcolor='#e5ecf6', width = 900)sel= c('state_abbrev','avg_hatecrimes_per_100k_fbi','share_vote_trump','gini_index')
hc= hate_crimes[,sel]
names(hc)= c('state','hatecrimes_fbi','votes_trump','gini')
hc= filter(hc, !is.na(hatecrimes_fbi))
labs= c("Votes for Trump","Gini","Hate crimes FBI")
scatterplot3js(hc$votes_trump, hc$gini, hc$hatecrimes_fbi, axisLabels=labs) |>
points3d(hc$votes_trump, hc$gini, hc$hatecrimes_fbi, color="red", pch=hc$state)You can plot data on the globe, e.g. arcs
f= read.table("../datasets/flights.txt", header=TRUE) #data flights from package threejs
f= mutate(f, dest=paste(round(f[,3],2),":",round(f[,4],2),sep="")) # approx location as factor
# select destinations with frequency >300
freq= group_by(f, dest) |>
summarise(nflights_dest=n())
fsel= left_join(f, freq) |>
filter(nflights_dest > 300)
globejs(arcs=fsel[,1:4])Or bars, e.g. city populations below
data(world.cities, package="maps")
cities <- world.cities[order(world.cities$pop, decreasing=TRUE)[1:1000],]
value <- 100 * cities$pop / max(cities$pop)
col <- colorRampPalette(c("cyan", "lightgreen"))(10)[floor(10 * value/100) + 1]
globejs(lat=cities$lat, long=cities$long, value=value, color=col, atmosphere=TRUE)datatable from package DT displays a dynamic table that can be searched, filtered, sorted… For a full description see here
Reproduce two examples of your choice from the plotly gallery
Turn in an html with your solution here. Name your file firstname_lastname.html
sf (simple feature) and terra define objects to store map-related data.
tmap generates maps. It accepts sf and terra objects, plus other spatial classes
Obtaining spatial data
Interactive maps
Basic functions
tm_shape: draw map boundaries
tm_fill: fill map with colours
tm_borders: draw region borders
tm_polygons: tm_fill + tm_borders
tm_raster: color cells (see tm_rgb)
tm_text: add text
tm_arrange: show various maps together
tmap_mode("plot"), tmap_mode("view"): static / interactive plots
Package spData includes dataset nz, an object of type sf
[1] "sf" "data.frame"
Simple feature collection with 16 features and 6 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 1090144 ymin: 4748537 xmax: 2089533 ymax: 6191874
Projected CRS: NZGD2000 / New Zealand Transverse Mercator 2000
First 10 features:
Name Island Land_area Population Median_income Sex_ratio
1 Northland North 12500.561 175500 23400 0.9424532
2 Auckland North 4941.573 1657200 29600 0.9442858
3 Waikato North 23900.036 460100 27900 0.9520500
4 Bay of Plenty North 12071.145 299900 26200 0.9280391
5 Gisborne North 8385.827 48500 24400 0.9349734
6 Hawke's Bay North 14137.524 164000 26100 0.9238375
7 Taranaki North 7254.480 118000 29100 0.9569363
8 Manawatu-Wanganui North 22220.608 234500 25000 0.9387734
9 Wellington North 8048.553 513900 32700 0.9335524
10 West Coast South 23245.456 32400 26900 1.0139072
geom
1 MULTIPOLYGON (((1745493 600...
2 MULTIPOLYGON (((1803822 590...
3 MULTIPOLYGON (((1860345 585...
4 MULTIPOLYGON (((2049387 583...
5 MULTIPOLYGON (((2024489 567...
6 MULTIPOLYGON (((2024489 567...
7 MULTIPOLYGON (((1740438 571...
8 MULTIPOLYGON (((1866732 566...
9 MULTIPOLYGON (((1881590 548...
10 MULTIPOLYGON (((1557042 531...
Load elevation data nz_elev from package spDataLarge
[1] "Name" "Island" "Land_area" "Population"
[5] "Median_income" "Sex_ratio" "geom"
Package geodata downloads geographical boundaries
gadm: administrative boundaries for any country
world: boundaries for countries in the world
Try also
wbstats package downloads data from the World Bank
Search available indicators with wb_search, wb_cache
Download specified indicators with wb_data
Get world boundaries, convert to sf and fix errors in object
Merge with World bank data by country (column NAME_0 in wsf, country in d)
Widgets allow user-based interactivity: all operations occur in your browser
Shiny apps also allow server-based interactions: operations are run by R in a server
To start a Shiny app
Create a directory for the app in your computer
Add a file app.R specifying how the app should look
For a primer on Shiny, see Hadley Wickam’s Mastering Shiny. See also
A minimal example
library(shiny)
ui= fluidPage( #specify user interface
selectInput("color", label="Color", choices= c("Blue","Red","Green")), #dropdown menu
checkboxInput("married", "Are you married?", value=FALSE), #check-box
)
server= function(input, output, session) { #server calculations
}
shinyApp(ui, server) #run the AppClick the “Run App” button in RStudio (or outside RStudio, call shiny::runApp() with the directory containing app.R)
In server you store all results in output, which is a list. The returned element must be the result of a render function:
renderPlot: return a plot (plotOutput in ui)
renderPrint: print results as in R console (paired with or verbatimOutput in ui)
renderText: return single string (paired with textOuput in ui)
renderTable: print a static table (tableOutput in ui). For short tables
renderDataTable: print a dynamic table (dataTableOutput in ui). For large tables/data
ui= fluidPage( #specify user interface
selectInput("color", label="Color", choices= c("Blue","Red","Green")), #dropdown menu
verbatimTextOutput("tablehomeworld"), #print output$tablehomeworld
)
server= function(input, output, session) { #server calculations
output$tablehomeworld= renderPrint({
data= filter(starwars, skin_color == input$color) #input$color from selectInput
table(data$homeworld)
})
}Print text. p("aaa"), strong("bold face text"), helpText("italics text")
Headers. titlePanel, h1("aaa"), h2("aaa"), h3("aaa")
Line break. br()
Update results button. submitButton("Get results")
Panel arrangement: sidebarLayout, mainPanel, sidebarPanel
animals= c("dog", "cat", "other") #define vector used multiple times below
ui <- fluidPage(
selectInput("animal", "Favourite animal?", animals, multiple=FALSE), #single choice
radioButtons("animal", "Favourite animal?", animals), #single choice
checkboxGroupInput("animal", "Animals you like?", animals) , #multiple choice
textInput("name", "What's your name?"), #short text box
textAreaInput("story", "Tell me about yourself", rows = 3) #larger text box
numericInput("num", "Number one", value = 0, min = 0, max = 100), #numeric box
sliderInput("num2", "Number two", value = 50, min = 0, max = 100), #slider
dateInput("dob", "When were you born?"), #single date
dateRangeInput("holiday", "Departure and return date?") #date range
fileInput("upload", NULL) #input file
)1st argument: identifier of the variable where input is stored
2nd argument: text to display
value: default value (optional)
Other arguments: specific on each input box
Easiest option: Shinyapps.io. Free plan allows 5 apps, 25 active hours
After creating an account and setting up the system, you can submit/update the app as follows
Other options available, such as hosting your own server, see here)
Check out ../examples/shinyapp/app.R. Using this as a template, create a Shiny app where
The user selects a country in the gapminder data (package gapminder)
A scatterplot of life expectancy vs. year is produced using only the data from that country, adding a linear regression line
The estimated regression coefficients are reported as separate text output